home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 11 / Cream of the Crop 11-1.iso / sound / sbplay25.zip / SOURCE.ZIP / _sbdma.asm < prev    next >
Assembly Source File  |  1995-10-09  |  26KB  |  1,244 lines

  1. _TEXT   SEGMENT WORD PUBLIC 'CODE'
  2. _TEXT   ENDS
  3.  
  4. _DATA SEGMENT WORD PUBLIC 'DATA'
  5.  
  6. _DATA   ENDS
  7.  
  8. CONST   SEGMENT WORD PUBLIC 'CONST'
  9. CONST   ENDS
  10.  
  11. _BSS    SEGMENT WORD PUBLIC 'BSS'
  12. ;GLOBAL UNINITIATED DATA GOES HERE
  13. _BSS    ENDS
  14.  
  15. DGROUP  GROUP CONST,_BSS,_DATA
  16. ;
  17.     ASSUME DS:DGROUP,SS:DGROUP
  18.  
  19. ;EXTRN EXTERNAL SUBROUTINE CALLS GO HERE
  20. ;
  21. ;
  22. ; _sbdma FAR Procedure for C Written by John A. Ball  October 3, 1995
  23. ;
  24. ; Copyright (c) 1995 John A. Ball
  25. ;
  26. ; You may copy and modify for your own use only!
  27. ; Please notify me of any errors or suggested enhancements.
  28. ;
  29. ; Plays back sound on the Sound Blaster DSP
  30. ;
  31. ;     int error sbdma(char *buffer, unsigned number_read,unsigned frequency,
  32. ;                    unsigned voc_pack);
  33. ;
  34. ;     error=sbdma(Buffer Pointer,Length,Frequency,voc_pack);
  35. ;
  36. ;       Buffer Pointer is a Far pointer to the sound buffer (64k max)
  37. ;       Length is the number of sound samples to play
  38. ;       Frequency is the playback frequency in hertz
  39. ;       voc_pack is the DSP dma mode
  40. ;       error=0 ok
  41. ;       error 1=DMA in use
  42. ;       error 2=IRQ/DMA not Found
  43. ;       error 3=DSP not responding
  44. ;       error 4=No DSP version
  45. ;       error 5=DMA channel not correct
  46. ;       error 6=IRQ in environment not correct
  47. ;       error 7=IRQ not valid for Sound Blaster
  48. ;       error 8=Number of samples is zero
  49. ;       error -1=Sound Halted
  50. ;
  51. _TEXT   SEGMENT
  52.     ASSUME CS:_TEXT
  53.     PUBLIC _sbdma,_sb_info,_sbdelay
  54.  
  55. _sbdma PROC FAR
  56.  
  57. PARMA           EQU [BP+6]      ;Sound Buffer Pointer
  58. PARMB           EQU [BP+10]     ;Number of samples to play (Length)
  59. PARMC           EQU [BP+12]     ;Frequency to playback samples
  60. PARMD           EQU [BP+14]     ;VOC Pack mode determines DSP DMA Mode
  61.  
  62.     PUSH BP                 ;Save stack Frame
  63.     MOV BP,SP
  64.     PUSHF
  65.     PUSH DS                 ;Save registers
  66.     PUSH ES
  67.     PUSH DI
  68.  
  69.     MOV AX,DGROUP           ;Set DS = our data segment
  70.     MOV DS,AX
  71.  
  72.     JMP OVERDATA
  73. _sb_info LABEL          WORD
  74. DMAFLAG                 DW 0    ;Flag to indicate DMA for DAC in use
  75. SB_IRQ                  DW -1   ;Sound Blaster IRQ (default 5)
  76. TIME_CONST              DW 0    ;DSP Time constant
  77. SAMPLES                 DW 0    ;Number of samples to play
  78. SB_IO_ADDR              DW 0    ;Sound Blaster I/O Address
  79. SB_VER                  DW 0    ;Sound Blaster DSP Version
  80. SB_DMA                  DW 1    ;Sound Blaster DMA Channel
  81. SB_TYPE                 DW 1    ;Sound Blaster board type
  82. ERROR                   DW 0    ;Sound Blaster Hardware Error
  83. TEMP                    DW 0    ;Temporary Storage
  84. BUFFER LABEL            WORD
  85. BUFFER_ADDR             DD 0    ;Pointer to Sound Data
  86. OLD_INTERRUPT   LABEL   WORD
  87. OLD_INTERRUPT_ADDR      DD 0    ;Location of previous IRQ 5 System Interrupt
  88. SAVEPIC                 DB 0    ;Contents of PIC 1
  89. SAVEPIC2                DB 0    ;Contents of PIC 2
  90. DMA_MODE                DB 014H ;DSP DMA mode
  91. DSP_SPEED               DB 0    ;Indicates whether highspeed dma is required
  92. SB_TEST                 DB 0    ;Indicates whether SB has been tested
  93. IRQ                     DW 0    ;IRQ from environment
  94. TESTBUFFER              DB 1 DUP(080H)
  95. OLD_ISR2 LABEL          WORD
  96. OLD_ISR2_ADDR           DD 0
  97. OLD_ISR3 LABEL          WORD
  98. OLD_ISR3_ADDR           DD 0
  99. OLD_ISR5 LABEL          WORD
  100. OLD_ISR5_ADDR           DD 0
  101. OLD_ISR7 LABEL          WORD
  102. OLD_ISR7_ADDR           DD 0
  103. OLD_ISR10 LABEL         WORD
  104. OLD_ISR10_ADDR          DD 0
  105. DSP_DMA_MODE            DB 014H, 075H, 077H, 017H
  106. DMA_PAGE                DB 087H, 083H, 081H, 082H
  107. CHANNELS                DB 1
  108.  
  109. OVERDATA:
  110.     CMP CS:DMAFLAG,0        ;Is DMAFLAG for DAC still in Use?
  111.     JZ OK
  112.     MOV CS:ERROR,1          ;DMA in use error
  113.     JMP EXIT2
  114. OK:
  115.     MOV AX,0                ;Reset error
  116.     MOV CS:ERROR,AX
  117.     CMP CS:SB_IRQ,-1        ;See if IRQ was available
  118.     JNE TEST_SB
  119.     CALL SBFINDIRQ          ;Find IRQ if none given
  120.     CMP CS:ERROR,0          ;Check for errors
  121.     JE TEST_SB
  122.     JMP EXIT2
  123. TEST_SB:
  124.     CMP CS:SB_TEST,0        ;Has SB been tested?
  125.     JNE OK1
  126.     MOV AX,CS:SB_IRQ        ;Get IRQ from environment
  127.     MOV CS:IRQ,AX           ;and save it for later
  128.     CALL SBFINDIRQ          ;Test IRQ/DMA operation
  129.     CMP CS:ERROR,0          ;Check to see if IRQ was found OK
  130.     JE IRQ_FOUND
  131.     JMP EXIT2
  132. IRQ_FOUND:
  133.     CMP CS:IRQ,0            ;See if IRQ was available
  134.     JE SBOK
  135.     MOV AX,CS:IRQ           ;if it was
  136.     CMP AX,CS:SB_IRQ        ;see if it is the same as the IRQ found
  137.     JE SBOK
  138.     MOV CS:ERROR,6          ;indicate wrong IRQ found in environment
  139. SBOK:   MOV CS:SB_TEST,1
  140.     CMP CS:ERROR,0          ;Check to see if IRQ was found ok?
  141.     JE OK1
  142.     JMP EXIT2
  143. OK1:
  144.     LES DI,PARMA            ;GET SOUND BUFFER POINTER
  145.     MOV CS:BUFFER,DI        ;AND SAVE
  146.     MOV DI,ES
  147.     MOV CS:BUFFER[2],DI
  148.  
  149.     MOV BX,PARMB            ;Get number of samples
  150.     CMP BX,0                ;Check for zero samples
  151.     JNE SAVE_S
  152.     MOV CS:ERROR,8
  153. SAVE_S: MOV CS:SAMPLES,BX       ;AND SAVE
  154.  
  155.     MOV BX,PARMC            ;Get frequency & calculate time constant
  156.     MOV DX,000FH
  157.     MOV AX,04240H
  158.     DIV BX
  159.     XOR AH,AH
  160.     SUB AH,AL
  161.     MOV AL,AH
  162.     XOR AH,AH
  163.     MOV CS:TIME_CONST,AX
  164.     CMP AL,0EAH             ;Check playback speed
  165.     JBE L18
  166.     MOV AX,00EAH            ;Limit playback speed to 44,100 hz
  167.     MOV CS:TIME_CONST,AX
  168. L18:    MOV CS:DSP_SPEED,01
  169.     CMP CS:SB_TYPE,-1       ;If SB is type -1
  170.     JE L24                  ;don't check dsp version
  171.     CMP CS:SB_VER,00
  172.     JNZ L24
  173.     CALL GET_DSP_VER        ;Check DSP version for maximum playback
  174. L24:    CMP CS:SB_VER,0200H
  175.     JAE L22
  176.     CMP CS:TIME_CONST,0D4H  ;Is playback greater than 22,727 hz?
  177.     JBE L22
  178.     MOV AX,00D4H            ;Limit to 22,727 hz if DSP version < 2.00
  179.     MOV CS:TIME_CONST,AX
  180. L22:    CMP CS:TIME_CONST,0D4H  ;Is playback less than 22,727 hz?
  181.     JA L19
  182.     MOV CS:DSP_SPEED,00
  183. L19:
  184.     MOV AX,PARMD            ;DSP DMA mode
  185.     CMP AX,255              ;If mode > 255 then sterio
  186.     JBE L33
  187.     MOV BX,2                ;Two channel
  188.     MOV CS:CHANNELS,BL
  189. L33:    AND AX,03               ;Limit to 4 modes
  190.     MOV BX,AX
  191.     MOV AL,DSP_DMA_MODE[BX]
  192.     MOV CS:DMA_MODE,AL
  193.  
  194.     CALL INSTALL_ISR        ;Install IRQ for Sound Blaster
  195.     MOV DX,CS:SB_IO_ADDR
  196.     ADD DX,0CH
  197.     MOV AL,040H             ;Set DSP time constant
  198.     CALL sbdspw
  199.     JNC A1
  200.     MOV CS:ERROR,3          ;DSP Error
  201.     JMP EXIT
  202. A1:     MOV AX,CS:TIME_CONST
  203.     CALL sbdspw
  204.     JNC A2
  205.     MOV CS:ERROR,3          ;DSP Error
  206.     JMP EXIT
  207. A2:
  208.     INC CS:DMAFLAG          ;SET DMAFLAG TO SHOW IN USE
  209.  
  210.     MOV DX,CS:BUFFER[2]     ;Convert Buffer Address to 64k Pages & Offset
  211.     MOV AX,CS:BUFFER
  212.     CALL PNT_TO_DMA
  213.     MOV CS:BUFFER,AX        ;AND SAVE
  214.     MOV CS:BUFFER[2],DX
  215.     CALL START_DMA
  216.  
  217.     CALL IO_WAIT            ;Wait for IRQ from Sound Blaster
  218.  
  219. EXIT:
  220.     CALL RESTORE_ISR        ;Restore IRQ & PIC
  221. EXIT2:  MOV AX,CS:ERROR         ;Get error if any
  222.     POP DI                  ;Restore registers
  223.     POP ES
  224.     POP DS
  225.     POPF
  226.     POP BP                  ;RESTORE BP
  227.     RET                     ;RETURN FAR
  228.  
  229. HANDLER:                        ;Interrupt for IRQ 5 comes here!
  230.     PUSHF
  231.     PUSH AX
  232.     PUSH BX
  233.     PUSH CX
  234.     PUSH DX
  235.     PUSH DS
  236.     PUSH ES
  237.     sti                     ;enable interrupts or computer will crash
  238.     CALL sbdsprt
  239.     MOV AL,020H
  240.     OUT 020H,AL
  241.     CMP CS:SAMPLES,0        ;Anymore samples?
  242.     JE L13
  243.     INC CS:BUFFER[2]        ;Increment to next page
  244.     MOV CS:BUFFER,0000      ;and set offset to zero
  245.     CALL START_DMA
  246.     JMP EXIT1
  247. L13:    DEC CS:DMAFLAG          ;Indicate it is
  248.  
  249. EXIT1:  POP ES
  250.     POP DS
  251.     POP DX
  252.     POP CX
  253.     POP BX
  254.     POP AX
  255.     POPF
  256.     IRET
  257.  
  258. _sbdma  ENDP
  259.  
  260. sbdspwt PROC NEAR
  261.  
  262. ;Write to DSP with timeout
  263.  
  264.     PUSH CX
  265.     MOV CX,200H
  266.     MOV AH,AL
  267. L1:     IN AL,DX
  268.     JMP $+2
  269.     OR AL,AL
  270.     JNS L2
  271.     LOOP L1
  272.     STC
  273.     JMP L3
  274. L2:     MOV AL,AH
  275.     OUT DX,AL
  276.     CLC
  277. L3:     POP CX
  278.     RET
  279.  
  280. sbdspwt  ENDP
  281.  
  282.  
  283. sbdspr PROC NEAR
  284.  
  285. ;Read DSP
  286.  
  287.     PUSH DX
  288.     MOV DX,CS:SB_IO_ADDR
  289.     ADD DL,0EH
  290.     SUB AL,AL
  291. L7:     IN AL,DX
  292.     JMP $+2
  293.     OR AL,AL
  294.     JNS L7
  295.     SUB DL,04H
  296.     IN AL,DX
  297.     POP DX
  298.     RET
  299.  
  300. sbdspr ENDP
  301.  
  302. sbdsprt PROC NEAR
  303.  
  304. ;Read DSP with timeout
  305.  
  306.     PUSH CX
  307.     PUSH DX
  308.     MOV DX,CS:SB_IO_ADDR
  309.     ADD DL,0EH
  310.     MOV CX,0200H
  311. L4:     IN AL,DX
  312.     JMP $+2
  313.     OR AL,AL
  314.     JS L5
  315.     LOOP L4
  316.     STC
  317.     JMP L6
  318. L5:     SUB DL,04H
  319.     IN AL,DX
  320.     CLC
  321. L6:     POP DX
  322.     POP CX
  323.     RET
  324.  
  325. sbdsprt ENDP
  326.  
  327. ;Write to DSP
  328.  
  329. sbdspw PROC NEAR
  330.  
  331.     PUSH CX
  332.     PUSH BX
  333.     MOV AH,AL
  334.     MOV BX,10
  335.     MOV CX,0ffffH           ;After timeout send value anyway
  336.     CLC
  337. L8:     IN AL,DX
  338.     JMP $+2
  339.     JMP $+2
  340.     OR AL,AL
  341.     JNS L8E
  342.     LOOP L8
  343.     DEC BX
  344.     OR BX,BX
  345.     JNZ L8
  346.     STC                     ;Set Carry to indicate timeout
  347. L8E:    MOV AL,AH
  348.     OUT DX,AL
  349.     POP BX
  350.     POP CX
  351.     RET
  352.  
  353. sbdspw ENDP
  354.  
  355. ;Convert FAR pointer to 64k pages and offset
  356.  
  357. PNT_TO_DMA PROC NEAR
  358.  
  359.     PUSH CX
  360.     MOV CL,4
  361.     ROL DX,CL
  362.     MOV CX,DX
  363.     AND DX,0FH
  364.     AND CX,0FFF0H
  365.     ADD AX,CX
  366.     ADC DX,0
  367.     POP CX
  368.     RET
  369.  
  370. PNT_TO_DMA ENDP
  371.  
  372. INSTALL_ISR PROC NEAR
  373.  
  374.     pushf
  375.     push ds
  376.     push es
  377.     CLI
  378.     MOV AX,CS:SB_IRQ
  379.     CMP AX,02
  380.     JE INSTALL
  381.     CMP AX,03
  382.     JE INSTALL
  383.     CMP AX,05
  384.     JE INSTALL
  385.     CMP AX,07
  386.     JE INSTALL
  387.     CMP AX,10
  388.     JE INSTALL
  389.     MOV CS:ERROR,7
  390.     MOV AX,5                ;Use default if error
  391. INSTALL:
  392.     CALL IRQ_VECT
  393.     MOV AH,035H             ;GET INTERRUPT for IRQ 5 AND SAVE IT
  394.     INT 21H
  395.     CLI
  396.     MOV CS:OLD_INTERRUPT,BX
  397.     MOV CS:OLD_INTERRUPT[2],ES
  398.     MOV AX,CS
  399.     MOV DS,AX
  400.     MOV AX,CS:SB_IRQ
  401.     CALL IRQ_VECT
  402.     MOV AH,025H             ;AND SET NEW INTERRUPT IRQ 5
  403.     LEA DX,HANDLER
  404.     INT 21H
  405.     pop es
  406.     pop ds
  407.     popf
  408.  
  409.     MOV AX,CS:SB_IRQ        ;IRQ 5
  410.     AND AL,08H
  411.     JZ L11
  412.     IN AL,021H              ;ENABLE IRQ from PIC
  413.     MOV CS:SAVEPIC,AL
  414.     AND AL,0FBH
  415.     OUT 021H,AL
  416.     MOV DX,00A1H
  417.     JMP L12
  418. L11:    MOV DX,021H
  419. L12:    MOV CX,CS:SB_IRQ        ;Sound Blaster IRQ
  420.     AND CL,07H
  421.     MOV AH,01
  422.     SHL AH,CL
  423.     NOT AH
  424.     IN AL,DX
  425.     MOV CS:SAVEPIC2,AL
  426.     AND AL,AH
  427.     OUT DX,AL
  428.     RET
  429.  
  430. INSTALL_ISR ENDP
  431.  
  432. RESTORE_ISR PROC NEAR
  433. ;
  434. ;Restore IRQ and PICs before exiting
  435. ;
  436.     PUSH AX
  437.     MOV AX,0
  438.     MOV CS:DMAFLAG,AX       ;Indicate DMA done
  439.     LDS DX,CS:OLD_INTERRUPT_ADDR
  440.     MOV AX,CS:SB_IRQ
  441.     CALL IRQ_VECT
  442.     CLI
  443.     MOV AH,025H             ;Restore old IRQ 5
  444.     INT 21h
  445.     CLI
  446.     MOV AX,CS:SB_IRQ        ;Restore PICs
  447.     AND AL,08
  448.     JZ L31
  449.     MOV AL,CS:SAVEPIC
  450.     OUT 021H,AL
  451.     MOV DX,00A1H
  452.     JMP L32
  453. L31:    MOV DX,0021H
  454. L32:    MOV AL,CS:SAVEPIC2
  455.     OUT DX,AL
  456.     POP AX
  457.     RET
  458.  
  459. RESTORE_ISR ENDP
  460.  
  461. IO_WAIT PROC NEAR
  462.  
  463. IOWAIT:
  464.     CMP CS:DMAFLAG,0        ;Is DMAFLAG for DAC still in Use?
  465.     JE DMA_DONE             ;Wait until done
  466.     MOV AX,CS:SB_DMA        ;Determine DMA count register
  467.     SHL AX,1
  468.     ADD AX,1
  469.     MOV DX,AX
  470.     IN AL,DX                ;Get DMA countdown
  471.     MOV AH,AL
  472.     IN AL,DX
  473.     XCHG AL,AH
  474.     CMP AX,4000             ;If greater than 6000 bytes to go
  475.     JG GIVE_CPU             ;then give up some cpu time
  476. WAIT_HERE:
  477.     CMP CS:DMAFLAG,0        ;Is DMAFLAG for DAC still in Use?
  478.     JE DMA_DONE             ;Wait until done
  479. ;        MOV AH,01H              ;See if key pressed
  480. ;        INT 16H
  481. ;        JZ WAIT_HERE
  482. ;        JMP KEY_PRESS
  483.     JMP WAIT_HERE
  484. GIVE_CPU:
  485.     CMP CS:DMAFLAG,0        ;Is DMAFLAG for DAC still in Use?
  486.     JE DMA_DONE             ;Wait until done
  487.     INT 28H                 ;MS-DOS idle handler
  488.     MOV AX,1680H
  489.     CMP CS:DMAFLAG,0        ;Is DMAFLAG for DAC still in Use?
  490.     JE DMA_DONE             ;Wait until done
  491.     INT 2FH                 ;MS-DOS idle call
  492.     CMP CS:DMAFLAG,0        ;Is DMAFLAG for DAC still in Use?
  493.     JE DMA_DONE             ;Wait until done
  494.     MOV AH,01H              ;See if key pressed
  495.     INT 16H
  496.     JZ IO_WAIT
  497. KEY_PRESS:
  498.     MOV AX,0
  499.     INT 16H                 ;Get the pressed key
  500.     CMP AL,1BH              ;See if ESC key hit?
  501.     JNE IO_WAIT
  502.     CALL HALT_DMA
  503.     CMP CS:ERROR,0          ;Check for error when halting DMA
  504.     JNE EXIT_IO
  505.     MOV CS:ERROR,0FFFFH     ;EXIT ERROR CODE
  506.     JMP EXIT_IO
  507. DMA_DONE:
  508.     MOV AX,0                ;OK
  509. EXIT_IO:
  510.     RET
  511.  
  512. IO_WAIT ENDP
  513.  
  514.  
  515. ; Start DMA transfer
  516.  
  517. START_DMA PROC NEAR
  518.  
  519.     MOV AX,CS:SB_DMA
  520.     AND AX,03H              ;Limit DMA channels to (0-3) 8-BIT
  521.     MOV CS:SB_DMA,AX
  522.     MOV AL,0FFH
  523.     OUT 0CH,AL              ;Clear byte pointer flip/flop
  524.     JMP $+2
  525.     MOV AX,CS:SB_DMA
  526.     ADD AL,04
  527.     OUT 0AH,AL              ;Set mask bit channel 1
  528.     JMP $+2
  529.     MOV AL,048H             ;Write mode
  530.     ADD AX,CS:SB_DMA        ;plus DMA channel
  531.     OUT 0BH,AL              ;Single mode select read channel 1
  532.     JMP $+2
  533.     MOV BX,CS:SB_DMA        ;Get DMA Page Register
  534.     MOV DX,0
  535.     MOV DL,CS:DMA_PAGE[BX]
  536.     MOV AX,CS:BUFFER[2]
  537.     OUT DX,AL               ;Set DMA page register
  538.     JMP $+2
  539.     MOV BX,CS:BUFFER
  540.     MOV DX,CS:SB_DMA        ;Determine memory address register
  541.     SHL DX,1
  542.     MOV AL,BL
  543.     OUT DX,AL               ;Set base and current address
  544.     JMP $+2
  545.     MOV AL,BH
  546.     OUT DX,AL
  547.     JMP $+2
  548.     XOR BX,-1               ;(65536-bx) = #bytes that can be transfered
  549.     MOV AX,CS:SAMPLES       ;Get # of samples & subtract 1
  550.     DEC AX                  ; # SAMPLES - 1
  551.     CMP AX,BX               ;# SAMPLES > # that can be DMA?
  552.     JBE L10
  553.     MOV AX,BX               ;If yes, use # that can be DMA
  554. L10:
  555.     CLI
  556.     PUSH AX
  557.     INC DX
  558.     OUT DX,AL               ;Base & current word count
  559.     JMP $+2
  560.     MOV AL,AH
  561.     OUT DX,AL
  562.     JMP $+2
  563.     POP AX
  564.     STI
  565.     PUSH AX                 ;Save number of samples to play
  566.     INC AX                  ;restore count
  567.     SUB CS:SAMPLES,AX       ;Subtract # that can be DMA
  568.     MOV AX,CS:SB_DMA
  569.     OUT 0AH,AL              ;Clear channel 1 mask bit to start DMA
  570.  
  571.     MOV DX,CS:SB_IO_ADDR
  572.     ADD DX,0CH
  573.     CMP CS:DSP_SPEED,01     ;Check for high speed mode
  574.     JNZ L21
  575.     MOV AL,048H
  576.     CALL sbdspw
  577.     JNC A11
  578.     MOV CS:ERROR,3          ;DSP not responding
  579.     JMP L20
  580. A11:
  581.     POP AX                  ;Get number of samples & send to DSP
  582.     MOV BX,AX
  583.     CALL sbdspw
  584.     JNC A12
  585.     MOV CS:ERROR,3          ;DSP not responding
  586.     JMP L20
  587. A12:
  588.     MOV AL,BH
  589.     CALL sbdspw
  590.     JNC A13
  591.     MOV CS:ERROR,3          ;DSP not responding
  592.     JMP L20
  593. A13:
  594.     MOV AL,091H             ;High Speed DMA mode
  595.     CALL SBDSPW
  596.     JNC A14
  597.     MOV CS:ERROR,3          ;DSP not responding
  598.     JMP L20
  599. A14:
  600.     JMP L20
  601. L21:
  602.     MOV AL,CS:DMA_MODE      ;DSP DMA mode for 8-bit DAC
  603.     CALL sbdspw
  604.     JNC A15
  605.     MOV CS:ERROR,3          ;DSP not responding
  606.     JMP L20
  607. A15:
  608.     POP AX                  ;Get number of samples & send to DSP
  609.     MOV BX,AX
  610.     CALL sbdspw
  611.     JNC A16
  612.     MOV CS:ERROR,3          ;DSP not responding
  613.     JMP L20
  614. A16:
  615.     MOV AL,BH
  616.     CALL sbdspw
  617.     JNC L20
  618.     MOV CS:ERROR,3          ;DSP not responding
  619. L20:    RET
  620. START_DMA ENDP
  621.  
  622. ; Calculate interrupt vector for Sound Blaster IRQ (5)
  623.  
  624. IRQ_VECT PROC NEAR
  625.  
  626.     PUSH BX
  627.     MOV BL,AL
  628.     AND BL,08
  629.     JZ L14
  630.     AND AL,07
  631.     ADD AL,070H
  632.     JMP L15
  633. L14:    ADD AL,08
  634. L15:    CBW
  635.     POP BX
  636.     RET
  637.  
  638. IRQ_VECT ENDP
  639.  
  640. SBFINDIRQ PROC NEAR
  641.  
  642. ; Install ISRs for possible Sound Blaster IRQs
  643.  
  644.     PUSHF
  645.     PUSH DX
  646.     PUSH CX
  647.  
  648.     MOV AX,CS
  649.     MOV DS,AX
  650.     MOV ES,AX
  651.  
  652.     MOV AL,02               ;IRQ 2
  653.     CALL IRQ_VECT           ;Get IRQ ISR address
  654.     PUSH AX
  655.     MOV AH,035H             ;Get old ISR address and save
  656.     INT 21H
  657.     MOV CS:OLD_ISR2,BX
  658.     MOV CS:OLD_ISR2[2],ES
  659.  
  660.     MOV AX,CS
  661.     MOV DS,AX
  662.     MOV ES,AX
  663.     POP AX
  664.     CLI
  665.     MOV AH,025H             ;AND SET NEW INTERRUPT IRQ 2
  666.     LEA DX,HANDLER2
  667.     INT 21H
  668.  
  669.     MOV AL,03               ;IRQ 3
  670.     CALL IRQ_VECT
  671.     PUSH AX
  672.     MOV AH,035H             ;Get old ISR address and save
  673.     INT 21H
  674.     MOV CS:OLD_ISR3,BX
  675.     MOV CS:OLD_ISR3[2],ES
  676.  
  677.     MOV AX,CS
  678.     MOV DS,AX
  679.     MOV ES,AX
  680.     POP AX
  681.     CLI
  682.     MOV AH,025H             ;AND SET NEW INTERRUPT IRQ 3
  683.     LEA DX,HANDLER3
  684.     INT 21H
  685.  
  686.     MOV AL,05               ;IRQ 5
  687.     CALL IRQ_VECT
  688.     PUSH AX
  689.     MOV AH,035H             ;Get old ISR address and save
  690.     INT 21H
  691.     MOV CS:OLD_ISR5,BX
  692.     MOV CS:OLD_ISR5[2],ES
  693.  
  694.     MOV AX,CS
  695.     MOV DS,AX
  696.     MOV ES,AX
  697.     POP AX
  698.     CLI
  699.     MOV AH,025H             ;AND SET NEW INTERRUPT IRQ 5
  700.     LEA DX,HANDLER5
  701.     INT 21H
  702.  
  703.     MOV AL,07               ;IRQ 7
  704.     CALL IRQ_VECT
  705.     PUSH AX
  706.     MOV AH,035H             ;Get old ISR address and save
  707.     INT 21H
  708.     MOV CS:OLD_ISR7,BX
  709.     MOV CS:OLD_ISR7[2],ES
  710.  
  711.     MOV AX,CS
  712.     MOV DS,AX
  713.     MOV ES,AX
  714.     POP AX
  715.     CLI
  716.     MOV AH,025H             ;AND SET NEW INTERRUPT IRQ 7
  717.     LEA DX,HANDLER7
  718.     INT 21H
  719.  
  720.     MOV AL,10               ;IRQ 10
  721.     CALL IRQ_VECT
  722.     PUSH AX
  723.     MOV AH,035H             ;Get old ISR address and save
  724.     INT 21H
  725.     MOV CS:OLD_ISR10,BX
  726.     MOV CS:OLD_ISR10[2],ES
  727.  
  728.     MOV AX,CS
  729.     MOV DS,AX
  730.     MOV ES,AX
  731.     POP AX
  732.     CLI
  733.     MOV AH,025H             ;AND SET NEW INTERRUPT IRQ 10
  734.     LEA DX,HANDLER10
  735.     INT 21H
  736.     IN AL,021H              ;ENABLE IRQS from PIC1
  737.     MOV CS:SAVEPIC,AL
  738.     AND AL,053H
  739.     OUT 021H,AL
  740.     IN AL,0A1H              ;ENABLE IRQS from PIC2
  741.     MOV CS:SAVEPIC2,AL
  742.     AND AL,0FBH
  743.     OUT 0A1H,AL
  744.  
  745.     MOV CS:SB_IRQ,0         ;Set IRQ to 0 and see if it changes
  746.  
  747.     CALL TEST_DMA           ;Start short DMA and see if it works
  748.     CMP CS:ERROR,0
  749.     JNE FOUNDIRQ
  750.  
  751.     MOV CX,0ffffh
  752. IRQWAIT:
  753.     INT 28
  754.     nop
  755.     MOV AX,1680H
  756.     INT 2FH
  757.     MOV AX,CS:SB_IRQ        ;Wait for interrupt to occur
  758.     CMP AX,02               ;with Time-Out
  759.     JE FOUNDIRQ
  760.     CMP AX,03
  761.     JE FOUNDIRQ
  762.     CMP AX,05
  763.     JE FOUNDIRQ
  764.     CMP AX,07
  765.     JE FOUNDIRQ
  766.     CMP AX,10
  767.     JE FOUNDIRQ
  768.     LOOP IRQWAIT
  769.  
  770.     IN AL,08H               ;Read DMA status register
  771.     JMP $+2
  772.     IN AL,08H               ;and again
  773.     CMP AL,0                ;Is status all clear?
  774.     JE IRQERR               ;If 0 then IRQ error
  775.     MOV CS:ERROR,5          ;DMA error
  776.     MOV DX,CS:SB_IO_ADDR
  777.     ADD DX,0CH
  778.     MOV AL,080H
  779.     OUT DX,AL               ;Send 1 byte to fix DSP
  780.     MOV CX,0ffffh           ;Wait for interrupt
  781. IRQWAIT1:
  782.     INT 28
  783.     MOV AX,1680H
  784.     INT 2FH
  785.     MOV AX,CS:SB_IRQ        ;Wait for interrupt to occur
  786.     CMP AX,02               ;with Time-Out
  787.     JE FOUNDIRQ
  788.     CMP AX,03
  789.     JE FOUNDIRQ
  790.     CMP AX,05
  791.     JE FOUNDIRQ
  792.     CMP AX,07
  793.     JE FOUNDIRQ
  794.     CMP AX,10
  795.     JE FOUNDIRQ
  796.     LOOP IRQWAIT1
  797.  
  798. IRQERR: MOV CS:ERROR,2          ;IRQ error (IRQ did not occur)
  799.  
  800. FOUNDIRQ:
  801.     CLI
  802.     MOV AL,CS:SAVEPIC       ;Restore IRQs as found
  803.     OUT 021H,AL
  804.     MOV AL,CS:SAVEPIC2
  805.     OUT 0A1H,AL
  806.  
  807.     MOV AL,02               ;IRQ 2
  808.     CALL IRQ_VECT
  809.     CLI
  810.     MOV AH,025H
  811.     LDS DX,CS:OLD_ISR2_ADDR
  812.     INT 21H                 ;Restore OLD vector
  813.  
  814.     MOV AL,03               ;IRQ 3
  815.     CALL IRQ_VECT
  816.     CLI
  817.     MOV AH,025H
  818.     LDS DX,CS:OLD_ISR3_ADDR
  819.     INT 21H                 ;Restore OLD vector
  820.  
  821.     MOV AL,05               ;IRQ 5
  822.     CALL IRQ_VECT
  823.     CLI
  824.     MOV AH,025H
  825.     LDS DX,CS:OLD_ISR5_ADDR
  826.     INT 21H                 ;Restore OLD vector
  827.  
  828.     MOV AL,07               ;IRQ 7
  829.     CALL IRQ_VECT
  830.     CLI
  831.     MOV AH,025H
  832.     LDS DX,CS:OLD_ISR7_ADDR
  833.     INT 21H                 ;Restore OLD vector
  834.  
  835.     MOV AL,10               ;IRQ 10
  836.     CALL IRQ_VECT
  837.     CLI
  838.     MOV AH,025H
  839.     LDS DX,CS:OLD_ISR10_ADDR
  840.     INT 21H                 ;Restore OLD vector
  841.  
  842.     MOV AX,CS:SB_IRQ        ;return with SB irq
  843.  
  844.     POP CX
  845.     POP DX
  846.     POPF
  847.     RET
  848.  
  849. HANDLER2:
  850.     PUSH DX
  851.     PUSH AX
  852.     MOV AX,CS
  853.     MOV DS,AX
  854.     MOV DX,CS:SB_IO_ADDR
  855.     ADD DX,0EH
  856.     IN AL,DX
  857.     MOV CS:SB_IRQ,02
  858.     MOV AL,020H
  859.     OUT 20H,AL
  860.     POP AX
  861.     POP DX
  862.     IRET
  863.  
  864. HANDLER3:
  865.     PUSH DX
  866.     PUSH AX
  867.     MOV AX,CS
  868.     MOV DS,AX
  869.     MOV DX,CS:SB_IO_ADDR
  870.     ADD DX,0EH
  871.     IN AL,DX
  872.     MOV CS:SB_IRQ,03
  873.     MOV AL,020H
  874.     OUT 20H,AL
  875.     POP AX
  876.     POP DX
  877.     IRET
  878.  
  879. HANDLER5:
  880.     PUSH DX
  881.     PUSH AX
  882.     MOV AX,CS
  883.     MOV DS,AX
  884.     MOV DX,CS:SB_IO_ADDR
  885.     ADD DX,0EH
  886.     IN AL,DX
  887.     MOV CS:SB_IRQ,05
  888.     MOV AL,020H
  889.     OUT 20H,AL
  890.     POP AX
  891.     POP DX
  892.     IRET
  893.  
  894. HANDLER7:
  895.     PUSH DX
  896.     PUSH AX
  897.     MOV AX,CS
  898.     MOV DS,AX
  899.     MOV DX,CS:SB_IO_ADDR
  900.     ADD DX,0EH
  901.     IN AL,DX
  902.     MOV CS:SB_IRQ,07
  903.     MOV AL,020H
  904.     OUT 20H,AL
  905.     POP AX
  906.     POP DX
  907.     IRET
  908.  
  909. HANDLER10:
  910.     PUSH DX
  911.     PUSH AX
  912.     MOV AX,CS
  913.     MOV DS,AX
  914.     MOV DX,CS:SB_IO_ADDR
  915.     ADD DX,0EH
  916.     IN AL,DX
  917.     MOV CS:SB_IRQ,10
  918.     MOV AL,020H
  919.     OUT 0A0H,AL
  920.     POP AX
  921.     POP DX
  922.     IRET
  923.  
  924. SBFINDIRQ ENDP
  925.  
  926. ;Get the Sound Blaster DSP version
  927.  
  928. GET_DSP_VER PROC NEAR
  929.  
  930.     PUSH CX
  931.     MOV CX,10
  932.     MOV DX,CS:SB_IO_ADDR
  933.     ADD DX,0CH
  934.     MOV AL,0E1H
  935.     CALL sbdspw
  936.     JNC A21
  937.     MOV CS:ERROR,3          ;DSP not responding
  938.     JMP L23
  939. A21:
  940. G1:     CALL sbdsprt
  941.     CMP AL,0AAH             ;Fix error in reading version
  942.     JNZ G2
  943.     LOOP G1
  944.     MOV CS:ERROR,4          ;DSP error not responding or no version
  945.     JMP L23
  946. G2:     MOV AH,AL
  947.     CALL sbdsprt
  948.     MOV CS:SB_VER,AX
  949.     MOV DX,CS:SB_IO_ADDR
  950.     ADD DX,0CH
  951.     MOV AL,0E1H
  952.     CALL sbdspw
  953.     JNC A22
  954.     MOV CS:ERROR,3          ;DSP not responding
  955.     JMP L23
  956. A22:
  957.     CALL sbdsprt
  958.     MOV AH,AL
  959.     CALL sbdsprt
  960.     CMP CS:SB_VER,AX
  961.     JE L23
  962.     MOV AX,00
  963.     MOV CS:SB_VER,AX
  964. L23:    POP CX
  965.     RET
  966.  
  967. GET_DSP_VER ENDP
  968.  
  969. ; Test Sound Blaster DMA transfer
  970.  
  971. TEST_DMA PROC NEAR
  972.  
  973.     MOV DX,CS
  974.     LEA AX,TESTBUFFER
  975.     CALL PNT_TO_DMA
  976.     PUSH AX
  977.     PUSH DX                 ;SAVE CONVERTED BUFFER ADDRESS
  978.     MOV AL,0FFH
  979.     OUT 00CH,AL             ;Clear byte pointer flip/flop
  980.     JMP $+2
  981.     MOV AX,CS:SB_DMA
  982.     ADD AL,04
  983.     OUT 0AH,AL              ;Set mask bit to channel
  984.     JMP $+2
  985.     MOV AL,048H
  986.     ADD AX,CS:SB_DMA
  987.     OUT 0BH,AL              ;Single mode select read channel
  988.     JMP $+2
  989.     MOV BX,CS:SB_DMA
  990.     MOV DX,0
  991.     MOV DL,CS:DMA_PAGE[BX]
  992.     POP BX                  ;Restore converted buffer address
  993.     MOV AL,BL
  994.     OUT DX,AL               ;Set DMA page register
  995.     JMP $+2
  996.     POP AX
  997.     MOV DX,CS:SB_DMA        ;Determine memory address register
  998.     SHL DX,1
  999.     OUT DX,AL               ;Set base and current address
  1000.     JMP $+2
  1001.     MOV AL,AH
  1002.     OUT DX,AL
  1003.     JMP $+2
  1004.     MOV BX,AX
  1005.     XOR BX,-1               ;(65536-BX) = # BYTES THAT CAN BE TRANSFERED
  1006.     MOV AX,0                ;# samples - 1
  1007.     CMP AX,BX
  1008.     JBE L100
  1009.     MOV AX,BX
  1010. L100:
  1011.     CLI
  1012.     INC DX
  1013.     OUT DX,AL             ;Base & current word count
  1014.     JMP $+2
  1015.     MOV AL,AH
  1016.     OUT DX,AL
  1017.     JMP $+2
  1018.     STI
  1019.     MOV AX,CS:SB_DMA
  1020.     OUT 0AH,AL            ;Clear channel 1 mask bit to start DMA
  1021.     JMP $+2
  1022.     MOV DX,CS:SB_IO_ADDR
  1023.     ADD DX,0CH
  1024.     MOV AL,040H
  1025.     CALL sbdspw            ;Set DSP time constant
  1026.     JNC A31
  1027.     MOV CS:ERROR,3         ;DSP not responding
  1028.     JMP A35
  1029. A31:
  1030.     MOV AL,064H
  1031.     CALL sbdspw
  1032.     JNC A32
  1033.     MOV CS:ERROR,3         ;DSP not responding
  1034.     JMP A35
  1035. A32:
  1036.     MOV AL,014H
  1037.     CALL sbdspw            ;DMA mode 8-bit DAC
  1038.     JNC A33
  1039.     MOV CS:ERROR,3         ;DSP not responding
  1040.     JMP A35
  1041. A33:
  1042.     MOV AX,0000            ;Number of bytes to be sent to DSP is 1
  1043.     CALL sbdspw            ;#samples to be sent to DSP
  1044.     JNC A34
  1045.     MOV CS:ERROR,3         ;DSP not responding
  1046.     JMP A35
  1047. A34:
  1048.     MOV AL,AH
  1049.     CALL sbdspw
  1050.     JNC A35
  1051.     MOV CS:ERROR,3         ;DSP not responding
  1052. A35:
  1053.     RET
  1054.  
  1055. TEST_DMA ENDP
  1056.  
  1057. ; Halt DMA transfer
  1058.  
  1059. HALT_DMA PROC NEAR
  1060.  
  1061.     MOV AX,CS               ;Set DS to CS
  1062.     MOV DS,AX
  1063.     MOV AX,0001
  1064.     CMP CS:DMAFLAG,0        ;Is DMAFLAG for DAC still in Use?
  1065.     JE H_END                ;Wait until done
  1066.     CMP CS:DSP_SPEED,01     ;In high speed?
  1067.     JNZ H_RESET
  1068.     CALL DSP_RESET          ;In high speed DSP must be reset first
  1069. H_RESET:
  1070.     CALL STOP_DMA
  1071.     CALL RESTORE            ;Restore IRQ, etc.
  1072. H_END:  RET
  1073.  
  1074. HALT_DMA ENDP
  1075.  
  1076. DSP_RESET PROC NEAR
  1077.  
  1078.     MOV DX,CS:SB_IO_ADDR
  1079.     ADD DL,06
  1080.     MOV AL,01
  1081.     OUT DX,AL
  1082.     IN AL,DX
  1083.     IN AL,DX
  1084.     IN AL,DX
  1085.     IN AL,DX
  1086.     SUB AL,AL
  1087.     OUT DX,AL
  1088.     MOV BL,10H
  1089. D_GET:  CALL sbdsprt
  1090.     CMP AL,0AAH
  1091.     JZ D_END
  1092.     DEC BL
  1093.     JNZ D_GET
  1094.     MOV AX,0002             ;I/O read/write fail
  1095.     STC
  1096.     JMP D_END1
  1097. D_END:  SUB AX,AX
  1098. D_END1: OR AX,AX
  1099.     RET
  1100.  
  1101. DSP_RESET ENDP
  1102.  
  1103. STOP_DMA PROC NEAR
  1104.  
  1105.     PUSHF
  1106.     PUSH SI
  1107.     MOV AH,0D0H             ;HALT DMA IN PROGRESS
  1108.     MOV BX, OFFSET DMAFLAG
  1109.     SUB CX,CX
  1110.     MOV DX,CS:SB_IO_ADDR
  1111.     ADD DL,0CH
  1112.     MOV SI,0FFFFH
  1113. S1:     STI
  1114.     CMP CL,[BX]             ;Is dsp in use?
  1115.     JZ SE
  1116.     CLI
  1117.     IN AL,DX
  1118.     OR AL,AL
  1119.     JS S2
  1120.     DEC SI
  1121.     JNZ S1
  1122. S5:     MOV CS:ERROR,3          ;DSP error (not responding)
  1123.     JMP SE
  1124. S2:     MOV SI,0FFFFH           ;Timeout Period
  1125. S3:     DEC SI
  1126.     JZ S5
  1127.     IN AL,DX
  1128.     OR AL,AL
  1129.     JS S3
  1130.     MOV AL,AH
  1131.     OUT DX,AL
  1132. SE:     POP SI
  1133.     POPF
  1134.     RET
  1135.  
  1136. STOP_DMA ENDP
  1137.  
  1138. RESTORE PROC NEAR
  1139.  
  1140.     MOV AL,04               ;DMA channel mask register
  1141.     ADD AX,CS:SB_DMA
  1142.     OUT 0AH,AL
  1143.     MOV AX,0
  1144.     MOV CS:DMAFLAG,AX       ;Indicate it is
  1145.     LDS DX,CS:OLD_INTERRUPT_ADDR
  1146.     MOV AX,CS:SB_IRQ
  1147.     CALL IRQ_VECT
  1148.     CLI
  1149.     MOV AH,025H             ;Restore old IRQ 5
  1150.     INT 21h
  1151.     CLI
  1152.     MOV AX,CS:SB_IRQ        ;Restore PICs
  1153.     AND AL,08
  1154.     JZ R10
  1155.     MOV AL,CS:SAVEPIC
  1156.     OUT 021H,AL
  1157.     MOV DX,00A1H
  1158.     JMP R11
  1159. R10:    MOV DX,0021H
  1160. R11:    MOV AL,CS:SAVEPIC2
  1161.     OUT DX,AL
  1162.     MOV DX,CS:SB_IO_ADDR    ;Acknowledge DSP interrupt
  1163.     ADD DL,0EH
  1164.     IN AL,DX
  1165.     RET
  1166.  
  1167. RESTORE ENDP
  1168.  
  1169. _sbdelay PROC FAR
  1170. ;
  1171. ; _sbdelay FAR Procedure for C Written by John A. Ball   January 9, 1994
  1172. ;
  1173. ; Causes a time delay using the Sound Blaster DSP
  1174. ;
  1175. ;       int error sbdelay(char tc, int period);
  1176. ;
  1177. PARMA1          EQU [BP+6]      ;Time Constant
  1178. PARMB1          EQU [BP+8]      ;Delay period
  1179.  
  1180.     PUSH BP                 ;Save stack Frame
  1181.     MOV BP,SP
  1182.     PUSHF
  1183.     PUSH DS                 ;Save registers
  1184.     PUSH ES
  1185.     PUSH DI
  1186.  
  1187.     MOV AX,DGROUP           ;Set DS = our data segment
  1188.     MOV DS,AX
  1189.  
  1190.     CMP CS:DMAFLAG,0        ;Is DMAFLAG for DAC still in Use?
  1191.     JZ DELAY
  1192.     MOV CS:ERROR,1          ;DMA in use error
  1193.     JMP EXIT3
  1194. DELAY:  CMP CS:SB_IRQ,2         ;See if there is a known valid IRQ
  1195.     JE DELAY1
  1196.     CMP CS:SB_IRQ,3
  1197.     JE DELAY1
  1198.     CMP CS:SB_IRQ,5
  1199.     JE DELAY1
  1200.     CMP CS:SB_IRQ,7
  1201.     JE DELAY1
  1202.     CMP CS:SB_IRQ,10
  1203.     JE DELAY1
  1204.     CALL SBFINDIRQ          ;Else find the IRQ
  1205.     MOV CS:SB_TEST,1
  1206.     CMP CS:ERROR,0          ;Check to see if IRQ was found
  1207.     JE DELAY1
  1208.     JMP EXIT3
  1209. DELAY1:
  1210.     CALL INSTALL_ISR        ;Install ISR for Sound Blaster IRQ & PIC
  1211.  
  1212.     MOV CX,PARMA1           ;GET TIME CONSTANT
  1213.     MOV BX,PARMB1           ;Get delay period
  1214.     MOV DX,CS:SB_IO_ADDR
  1215.     ADD DX,0CH
  1216.     MOV AL,040H             ;Set Time Constant
  1217.     CALL sbdspw
  1218.     MOV AL,CL
  1219.     CALL sbdspw
  1220.     MOV AL,080H             ;Set Silence Length
  1221.     CALL sbdspw
  1222.     MOV AL,BL               ;and send Period
  1223.     CALL sbdspw
  1224.     MOV AL,BH
  1225.     CALL sbdspw
  1226.  
  1227.     INC CS:DMAFLAG          ;SET DMAFLAG TO SHOW IN USE
  1228.  
  1229.     CALL IO_WAIT            ; and wait for IRQ to occur
  1230.  
  1231.     CALL RESTORE_ISR        ; RESTORE ISR and PIC
  1232. EXIT3:  MOV AX,CS:ERROR
  1233.     POP DI                  ;Restore registers
  1234.     POP ES
  1235.     POP DS
  1236.     POPF
  1237.     POP BP                  ;RESTORE BP
  1238.     RET
  1239. _sbdelay ENDP
  1240.  
  1241. _TEXT   ENDS
  1242.     END
  1243.  
  1244.